package com.derbysoft.synxisadapter.dswitch3.translator;

import com.derbysoft.core.utils.DateTimeUtils;
import com.derbysoft.dswitch.dto.hotel.avail.AvailRoomStayDTO;
import com.derbysoft.dswitch.dto.hotel.avail.HotelAvailRoomStayDTO;
import com.derbysoft.dswitch.dto.hotel.avail.RatePlanDTO;
import com.derbysoft.dswitch.dto.hotel.common.ChargeItemDTO;
import com.derbysoft.dswitch.dto.hotel.common.RoomRateDTO;
import com.derbysoft.dswitch.dto.hotel.common.StayDateRangeDTO;
import com.derbysoft.dswitch.dto.hotel.common.TotalDTO;
import com.derbysoft.dswitch.dto.hotel.reservation.HotelReservationRS;
import com.derbysoft.dswitch.dto.hotel.reservation.ReservationRoomStayDTO;
import com.derbysoft.dswitch.remote.hotel.dto.HotelAvailResponse;
import com.derbysoft.dswitch.remote.hotel.dto.HotelReservationRequest;
import com.derbysoft.dswitch.remote.hotel.dto.HotelReservationResponse;
import com.derbysoft.synxisadapter.commons.model.Channel;
import com.derbysoft.synxisadapter.dswitch2.translator.Translator;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * User: jason
 * Date: 2012-11-28
 */
@Component("ds3HotelPreviewRSTranslator")
public class HotelPreviewRSTranslator extends AbstractTranslator
        implements Translator<HotelAvailResponse, HotelReservationResponse, HotelReservationRequest> {

    @Override
    public HotelReservationResponse translate(HotelAvailResponse availResponse, HotelReservationRequest request) {
        HotelReservationResponse response = new HotelReservationResponse();
        response.setTaskId(request.getHeader().getTaskId());
        if (availResponse.getError() != null) {
            response.setError(availResponse.getError());
            return response;
        }
        AvailRoomStayDTO availRoomStayDTO = findRoomStay(availResponse, request);
        if (availRoomStayDTO == null) {
            response.setError(noAllotmentErrorDTO(request));
            return response;
        }
        response.setHotelReservationRS(createReservationRS(availRoomStayDTO, request));
        return response;
    }

    private HotelReservationRS createReservationRS(AvailRoomStayDTO availRoomStayDTO, HotelReservationRequest request) {
        HotelReservationRS hotelReservationRS = new HotelReservationRS();
        hotelReservationRS.setErsp(request.getHotelReservationRQ().getErsp());
        hotelReservationRS.setHotelReservation(request.getHotelReservationRQ().getHotelReservation());
        RoomRateDTO roomRateDTO = availRoomStayDTO.getRoomRate();
        RatePlanDTO ratePlanDTO = availRoomStayDTO.getRatePlan();
        ReservationRoomStayDTO reservationRoomStayDTO = hotelReservationRS.getHotelReservation().getRoomStaysList().get(0);
        reservationRoomStayDTO.setRoomRate(roomRateDTO);
        StayDateRangeDTO stayDateRange = request.getHotelReservationRQ().getHotelReservation().getRoomStaysList().get(0).getStayDateRange();
        Channel channel = loadChannel(request.getHeader().getSource());
        reservationRoomStayDTO.setTotal(translateTotal(channel, availRoomStayDTO.getCurrency(), stayDateRange, roomRateDTO, ratePlanDTO));
        hotelReservationRS.setCancelPolicy(ratePlanDTO.getCancelPolicy());
        return hotelReservationRS;
    }

    private TotalDTO translateTotal(Channel channel, String currency,
                                    StayDateRangeDTO stayDateRangeDTO, RoomRateDTO roomRateDTO, RatePlanDTO ratePlanDTO) {
        TotalDTO totalDTO = new TotalDTO();
        totalDTO.setCurrency(currency);
        if (channel.isInclusiveTaxRate()) {
            totalDTO.setAmountAfterTax(RateCalculator.calcAmountAfterTax(roomRateDTO).doubleValue());
        } else {
            totalDTO.setAmountBeforeTax(RateCalculator.calcAmountBeforeTax(roomRateDTO).doubleValue());
            if (CollectionUtils.isNotEmpty(ratePlanDTO.getTaxesList())) {
                totalDTO.setTaxesList(createTaxes(stayDateRangeDTO, RateCalculator.calcTotalTax(roomRateDTO, ratePlanDTO)));
            }
        }
        return totalDTO;
    }

    private List<ChargeItemDTO> createTaxes(StayDateRangeDTO stayDateRangeDTO, BigDecimal totalTax) {
        ArrayList<ChargeItemDTO> taxes = new ArrayList<ChargeItemDTO>();
        ChargeItemDTO tax = new ChargeItemDTO();
        tax.setValue(totalTax.doubleValue());
        tax.setStartDate(stayDateRangeDTO.getCheckin());
        tax.setEndDate(DateTimeUtils.formatDate(DateTimeUtils.addDays(DateTimeUtils.parse(stayDateRangeDTO.getCheckout()), -1)));
        tax.setType(ChargeItemDTO.ChargeType.FIXED);
        taxes.add(tax);
        return taxes;
    }

    private AvailRoomStayDTO findRoomStay(HotelAvailResponse availResponse, HotelReservationRequest request) {
        String hotelCode = request.getHotelReservationRQ().getHotelReservation().getHotelCode();
        HotelAvailRoomStayDTO hotelAvailRoomStayDTO = findHotelAvailRoomStayDTO(availResponse.getHotelAvailRS().getHotelAvailRoomStaysList(), hotelCode);
        if (hotelAvailRoomStayDTO == null) {
            return null;
        }
        String ratePlan = request.getHotelReservationRQ().getHotelReservation().getRoomStaysList().get(0).getRatePlanCode();
        String roomType = request.getHotelReservationRQ().getHotelReservation().getRoomStaysList().get(0).getRoomTypeCode();

        for (AvailRoomStayDTO roomStay : hotelAvailRoomStayDTO.getRoomStaysList()) {
            if (roomStay.getRatePlan().getCode().equals(ratePlan) && roomStay.getRoomType().getCode().equals(roomType)) {
                return roomStay;
            }
        }
        return null;
    }

    private HotelAvailRoomStayDTO findHotelAvailRoomStayDTO(List<HotelAvailRoomStayDTO> hotelAvailRoomStays, String hotelCode) {
        for (HotelAvailRoomStayDTO hotelAvailRoomStay : hotelAvailRoomStays) {
            if (hotelAvailRoomStay.getHotelCode().equals(hotelCode)) {
                return hotelAvailRoomStay;
            }
        }
        return null;
    }
}
